// A very stupid predictor.  It will always predict not taken.

#include "./predictor.h"
#include <iostream>

void init_predictor ()
{
    Alpha::GlobalHistory = 0;
    for (int i = 0; i < LOCALSIZE; i++)
    {
        Alpha::LocalHistory[i] = 0;
        Alpha::LocalPrediction[i] = 0;
    }
    for (int i = 0; i < GLOBALSIZE; i++)
    {
        Alpha::GlobalPrediction[i] = 0;
        Alpha::Chooser[i] = 0;
    }
}

bool make_prediction (unsigned int pc)
{
    // Chooser: 10-11 use global table
    if (Alpha::Chooser[Alpha::GlobalHistory] >= 2)
    {
        // 2-bit saturating counter 10-11 : Taken, Not Taken otherwise
        if (Alpha::GlobalPrediction[Alpha::GlobalHistory] >= 2)
            return true;
        return false;
    }

    // Chooser 00-01 use local table
    // 3-bit saturating countet 100-111 taken, Not Taken otherwise
    if (Alpha::LocalPrediction[Alpha::LocalHistory[pc%LOCALSIZE]] >= 4)
        return true;
    return false;
}

void train_predictor (unsigned int pc, bool outcome)
{
    // update Chooser
    if (outcome)
    {
        if (Alpha::GlobalPrediction[Alpha::GlobalHistory] >= 2 &&
            Alpha::LocalPrediction[Alpha::LocalHistory[pc%LOCALSIZE]] < 4)
            Alpha::Chooser[Alpha::GlobalHistory] += 1;
        else if (Alpha::GlobalPrediction[Alpha::GlobalHistory] < 2 &&
            Alpha::LocalPrediction[Alpha::LocalHistory[pc%LOCALSIZE]] >= 4)
            Alpha::Chooser[Alpha::GlobalHistory] -= 1;
 
    }
    else
    {
        if (Alpha::GlobalPrediction[Alpha::GlobalHistory] >= 2 &&
            Alpha::LocalPrediction[Alpha::LocalHistory[pc%LOCALSIZE]] < 4)
            Alpha::Chooser[Alpha::GlobalHistory] -= 1;
        else if (Alpha::GlobalPrediction[Alpha::GlobalHistory] < 2 &&
            Alpha::LocalPrediction[Alpha::LocalHistory[pc%LOCALSIZE]] >= 4)
            Alpha::Chooser[Alpha::GlobalHistory] += 1;
    }
    if (Alpha::Chooser[Alpha::GlobalHistory] < 0)
        Alpha::Chooser[Alpha::GlobalHistory] = 0;
    if (Alpha::Chooser[Alpha::GlobalHistory] > 3)
        Alpha::Chooser[Alpha::GlobalHistory] = 3;


    // update Local Predictor
    if (outcome)
        Alpha::LocalPrediction[Alpha::LocalHistory[pc%LOCALSIZE]] += 1;
    else
        Alpha::LocalPrediction[Alpha::LocalHistory[pc%LOCALSIZE]] -= 1;
    if (Alpha::LocalPrediction[Alpha::LocalHistory[pc%LOCALSIZE]] < 0)
        Alpha::LocalPrediction[Alpha::LocalHistory[pc%LOCALSIZE]] = 0;
    if (Alpha::LocalPrediction[Alpha::LocalHistory[pc%LOCALSIZE]] > 7)
        Alpha::LocalPrediction[Alpha::LocalHistory[pc%LOCALSIZE]]  = 7;
    
    // update Global Predictor
    if (outcome)
        Alpha::GlobalPrediction[Alpha::GlobalHistory] += 1;
    else
        Alpha::GlobalPrediction[Alpha::GlobalHistory] -= 1;
    if (Alpha::GlobalPrediction[Alpha::GlobalHistory] < 0)
        Alpha::GlobalPrediction[Alpha::GlobalHistory] = 0;
    if (Alpha::GlobalPrediction[Alpha::GlobalHistory] > 3)
        Alpha::GlobalPrediction[Alpha::GlobalHistory] = 3;

    // record the local history
    Alpha::LocalHistory[pc%LOCALSIZE] = Alpha::LocalHistory[pc%LOCALSIZE] << 1;
    if (outcome)
        Alpha::LocalHistory[pc%LOCALSIZE] += 1;
    // truncate to 10 bits
    Alpha::LocalHistory[pc%LOCALSIZE] &= 1023;
   
    // record the global history
    Alpha::GlobalHistory = Alpha::GlobalHistory << 1;
    if (outcome)
        Alpha::GlobalHistory += 1;
    //truncate to 12 bits
    Alpha::GlobalHistory &= 4095;
}
